home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Multimedia / Resource Library: Multimedia.iso / maestro / source / timeline / play.c < prev    next >
Encoding:
Text File  |  1993-06-15  |  55.5 KB  |  1,223 lines

  1. /*
  2.  * Copyright (c) 1990, 1991 Stanford University
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software and 
  5.  * its documentation for any purpose is hereby granted without fee, provided
  6.  * that (i) the above copyright notices and this permission notice appear in
  7.  * all copies of the software and related documentation, and (ii) the name
  8.  * Stanford may not be used in any advertising or publicity relating to
  9.  * the software without the specific, prior written permission of
  10.  * Stanford.
  11.  * 
  12.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  13.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  14.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  15.  *
  16.  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
  17.  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
  18.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
  19.  * ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
  20.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  21.  * SOFTWARE.
  22.  */
  23.  
  24. /* $Header: /Source/Media/collab/TimeLine/RCS/play.c,v 1.16 92/10/30 16:22:49 drapeau Exp $ */
  25. /* $Log:    play.c,v $
  26.  * Revision 1.16  92/10/30  16:22:49  drapeau
  27.  * Fixed minor error in ResumeOrPerformSelections(); before, not all edits would
  28.  * necessarily be played, perhaps due to the way in which the instrument
  29.  * pointer was being advanced.
  30.  * 
  31.  * Revision 1.15  92/10/07  15:15:51  drapeau
  32.  * New version of TimeLine adds two command-line options: "-autoPlay" and
  33.  * "-autoQuit".  "-autoPlay" indicates that TimeLine should immediately begin
  34.  * performance of the document given on the command line (if there is one).
  35.  * "-autoQuit" indicates that TimeLine should quit immediately after
  36.  * completion of performance of the document given on the command line.
  37.  * The "-autoQuit" flag is only valid if "-autoPlay" is also specified.
  38.  * These two options were added to support automated demos using TimeLine.
  39.  * 
  40.  * Revision 1.14  92/10/01  14:49:52  drapeau
  41.  * A number of minor changes were made to fix errors in the playback mechanism.
  42.  * These errors were introduced when the "Synchronization Hints" feature was
  43.  * added.  The addition of this feature complicated the algorithm for
  44.  * determining when it is appropriate to send OpenDocument, SetSelection,
  45.  * PerformSelection, and other related messages to the applications comprising
  46.  * a TimeLine document.  Most of the error fixes had to do with testing for
  47.  * boundary conditions.
  48.  * Also, when the document has resumed playback (after it was previously put
  49.  * into a Pause mode), TimeLine will now correctly change the status of the
  50.  * document, returning it to PlayMode or PlaySelectedMode, whichever is
  51.  * appropriate.
  52.  * Several cosmetic changes were made to improve code readability and ANSI
  53.  * compliance.
  54.  * 
  55.  * Revision 1.13  92/09/29  18:02:21  drapeau
  56.  * Modified TimerNotify(): modified a test that had previously failed to
  57.  * test for a boundary condition.
  58.  * Also, modified ResumeOrPerformSelections() to account for both complete
  59.  * selections and partial selections.  Mostly, it was a matter of taking the
  60.  * current note's offset into account when deciding whether it was time to
  61.  * perform a selection.
  62.  * Also, made slight cosmetic changes in the code to improve readability.
  63.  * 
  64.  * Revision 1.12  92/09/24  17:20:01  drapeau
  65.  * Major changes have been made to this file, both to improve readability and to
  66.  * implement synchronized documents.
  67.  * The function most affected by the coding changes is the TimerNotify()
  68.  * function, the workhorse of TimeLine's playback mechanism.
  69.  * TimerNotify() now checks if the current document has synchronization
  70.  * information; if so, the function schedules OpenDocument and SetSelection
  71.  * messages according to the sync hints in each note to be played.  If there is
  72.  * no sync. information, TimerNotify will still schedule OpenDocument and
  73.  * SetSelection messages correctly, but there will be delays as with the old
  74.  * style documents.
  75.  * In addition, if the author has chosen to generate sync hints, TimerNotify()
  76.  * will note this and generate sync info for the document, then mark it as
  77.  * modified.
  78.  * TimerNotify() has been broken into several functions to make the code simpler
  79.  * to read.  Accordingly, comments have been updated to reflect the new
  80.  * structure of the code.
  81.  * The code now uses the new Sender method "SenderSynchronousMessaging()" to
  82.  * implement the gathering and use of sync hints.  During the gathering of
  83.  * sync hints, and for documents with no sync hints, synchronous messaging is
  84.  * used.  This causes TimeLine to block on each note, waiting for completion of
  85.  * each message sent to the media editors in a document.  During playback of a
  86.  * document that has sync hints, asynchronous messaging is used.  In this case,
  87.  * all messages sent by TimeLine return immediately, since the RPC's being sent
  88.  * do not wait for replies from the media editors receiving those messages.
  89.  * This enables TimeLine to schedule OpenDocument/SetSelection messages in
  90.  * advance of the performance times for individual notes, thus helping achieve
  91.  * documents that perform more closely to the wishes of authors.  Ideally, such
  92.  * documents are more "in sync" than documents with no sync hints.
  93.  * 
  94.  * Other changes were made to the code to improve readability and ANSI
  95.  * compliance.
  96.  * 
  97.  * Revision 1.11  92/07/14  12:32:20  drapeau
  98.  * Minor change to PlayStop() function: removed a feature that would
  99.  * automatically quit the application when the end of a document was reached.
  100.  * This feature was installed on request by an outside author, who wanted to
  101.  * start TimeLine from the command line.  In the future, this feature might
  102.  * be added again, but with a command-line option to enable it.
  103.  * 
  104.  * Revision 1.10  92/05/29  14:42:31  drapeau
  105.  * Modified code to track new name of the MAEstro "Selection" structure;
  106.  * it is now named "MAESelection".
  107.  * 
  108.  * Revision 1.1  92/01/09  16:26:56  drapeau
  109.  * Made slight modifications to make code more ANSI-compliant.
  110.  * 
  111.  * Revision 1.0  91/09/30  17:02:28  chua
  112.  * Update to version 1.0
  113.  * 
  114.  * Revision 0.78  91/09/25  13:51:16  chua
  115.  * Changed the instrument field, instInfo, to editInfo.
  116.  * Changed InstrumentInfo to EditInfo.
  117.  * 
  118.  * Revision 0.77  91/09/23  17:15:13  chua
  119.  * Included a new function, Play, which is called when the play button is
  120.  * pressed (the one with the icon).  If the TimeLine is in play mode, pressing
  121.  * this button has no effect.  If the TimeLine is in pause mode, pressing
  122.  * this button will resume play.  If the TimeLine is in stop mode, pressing
  123.  * this button will play selected region, or from insertion point, or
  124.  * whole document (in order of priority).
  125.  * 
  126.  * PlayStop is now only called when we actually want to stop the timer
  127.  * completely (when TimeLine is in stop mode).  During pause, the timer
  128.  * continues functioning.
  129.  * 
  130.  * In TimerNotify, check every second to see if either the pause or play 
  131.  * button needs to have their color changed.  Also, if the TimeLine is
  132.  * in pause mode, simply update the virtual clock and exit.  Do not check
  133.  * if notes need to be played.
  134.  * 
  135.  * Revision 0.76  91/09/20  15:03:47  chua
  136.  * Added calls to BusyCursor and NormalCursor to change the cursor type when
  137.  * doing OpenDoc and SetSelection.
  138.  * 
  139.  * Revision 0.75  91/09/19  17:29:03  chua
  140.  * Make sure that variables are initialized properly.  Change formatting slightly,
  141.  * so that (if, for, while) statements with only one statement in them will not have
  142.  * braces.
  143.  * 
  144.  * Revision 0.74  91/08/26  16:38:05  chua
  145.  * In TimerNotify, if a pause has been detected, exit from the notify procedure.
  146.  * 
  147.  * Revision 0.73  91/08/21  16:59:45  chua
  148.  * In TimerNotify and PreLoad, if SetSelection does not return 0, do a number of Ping
  149.  * commands (determined by the constant PingTimes) to allow the app time to do the
  150.  * set selection.
  151.  * 
  152.  * Revision 0.72  91/08/20  16:18:41  chua
  153.  * Declare startApp and numApps as globals to make them visible to the functions in 
  154.  * stop.c
  155.  * 
  156.  * In TimerNotify (line 413), a check is made to see that CheckAppOpen returns OK.
  157.  * 
  158.  * Revision 0.71  91/08/19  19:21:02  chua
  159.  * In TimerNotify, set the currentNote pointer to point to where the current note is,
  160.  * so that resume will work properly.
  161.  * 
  162.  * Revision 0.70  91/08/16  17:03:06  chua
  163.  * Made changes to the comments header for TimerNotify.  Also, removed the gettimeofday
  164.  * variables and the function AllocateTimerSpace as these are no longer needed.
  165.  * 
  166.  * Moved the DrawPlaybackHead routine to drawCanvas.c
  167.  * 
  168.  * Add a new function, PreLoad, which will do a OpenDoc and SetSelection message call
  169.  * for the first notes that are to be played, when a play operation is in progress.
  170.  * 
  171.  * Moved the StopHandler and PauseResumeHandler to stop.c
  172.  * 
  173.  * Revision 0.69  91/08/14  12:13:07  chua
  174.  * Added a new function PreLoad, which will do a OpenDoc and SetSelection for the first notes
  175.  * in each application during the start of a play operation.
  176.  * 
  177.  * Do the necessary modifications in TimerNotify to support the above. (involves testing if the
  178.  * note is not the first one before doing the OpenDoc and SetSelection).
  179.  * 
  180.  * Revision 0.68  91/08/08  15:15:24  chua
  181.  * In TimerNotify, before doing a OpenDoc and SetSelection, check that the
  182.  * application is open first.
  183.  * 
  184.  * Revision 0.67  91/08/08  14:28:48  chua
  185.  * Before a play operation, if the applications are not muted, check that they are all 
  186.  * still open before proceeding.  This is done by trying to create a new sender with each
  187.  * application.  
  188.  * 
  189.  * Also, in the areas where there are network messages sent, such as Pause, Halt, etc, 
  190.  * do not send the messages if the application is muted.
  191.  * 
  192.  * Revision 0.66  91/08/05  16:53:30  chua
  193.  * Deleted the RepaintCanvas routine, as it is no longer necessary.  In places where it
  194.  * is called, just call the ScrollToFirstQuarter routine, which will do the necessary
  195.  * repaint as well.
  196.  * 
  197.  * Revision 0.65  91/08/05  14:40:33  chua
  198.  * In the PlayHandler, PlayFromHandler and PlaySelectedHandler, a check is made to see that
  199.  * the applications are still alive before attempting to play.  This is done by trying to 
  200.  * create a sender to the application.  If the NewSender call returns a NULL, then the
  201.  * application is no longer alive and the play operation is aborted.
  202.  * 
  203.  * Revision 0.64  91/08/05  13:05:34  chua
  204.  * In the PlayHandler and PlayFromPoint routine, call the ScrollToFirstQuarter routine to check if
  205.  * the canvas needs to be scrolled, so that the playback head will be visible.
  206.  * 
  207.  * Revision 0.63  91/07/22  15:22:31  chua
  208.  * In the TimerNotify, also check the pause list to see if a pause marker exist at the 
  209.  * current time. If so, call the PauseResumeHandler to pause the applications.
  210.  * 
  211.  * Revision 0.62  91/07/17  16:35:00  chua
  212.  * Reformatted the braces for the switch statements.
  213.  * 
  214.  * Revision 0.61  91/07/17  10:34:58  chua
  215.  * The definition of the playback head position, lastX, has been changed.   It now
  216.  * indicates its position in terms of pixel value at the lowest zoom level.
  217.  * So code where lastX or the DrawPlaybackHead function is called will have some
  218.  * changes to accomodate this change.
  219.  * 
  220.  * In the TimerNotify procedure, check if we need to remap the canvas while playing.
  221.  * 
  222.  * In the DrawPlaybackHead procedure, check that the playback head is in the current
  223.  * canvas before drawing it.
  224.  * 
  225.  * Revision 0.60  91/07/09  18:25:05  chua
  226.  * Made changes to the startX, endX variables so that they now store the position at the
  227.  * largest zoom level (zoom level = 1).   Thus, the appropriate multiplication or
  228.  * division by the zoom level has to be made when these variables are used.  This will
  229.  * include lastX (position of the playback head) as well.
  230.  * 
  231.  * Revision 0.59  91/07/09  17:02:41  chua
  232.  * Removed a redundant variable.
  233.  * 
  234.  * Revision 0.58  91/07/01  11:02:37  chua
  235.  * Changed PlayFromHandler so that if playback head is not present, it is set to zero. 
  236.  * The reason this is done is because the default selection in the play menu button is
  237.  * now "Play from insertion point".
  238.  * 
  239.  * Revision 0.57  91/06/28  17:26:47  chua
  240.  * Changed a line in TimerNotify so that OpenDoc and SetSelection gets 
  241.  * called when playing from the middle of a segment.
  242.  * 
  243.  * Revision 0.56  91/06/27  14:16:55  chua
  244.  * Changed a line in TimerNotify, so that as long as note->start is the same as the
  245.  * virtualClock, the TimeLine Editor will send OpenDoc and SetSelection messages to the
  246.  * application.
  247.  * 
  248.  * Revision 0.55  91/06/27  11:51:12  chua
  249.  * Modified the play and pause/resume handlers to correctly set the mode. Resume will now
  250.  * work correctly.
  251.  * 
  252.  * Revision 0.54  91/06/26  16:47:17  chua
  253.  * Added code to handle zooming and also the new protocol items, which are playing partial
  254.  * selections, pause selection, halt selection and resume selection.
  255.  * Refer to the comment headings within the code for more details.
  256.  * 
  257.  * Revision 0.53  91/06/04  17:37:26  chua
  258.  * Added the copyright comments in the beginning of the file.
  259.  * 
  260.  * Revision 0.52  91/06/03  11:12:04  chua
  261.  * Make changes to accomodate multiple documents.  This involves identifying
  262.  * which is the current active window, that is, the one where the last mouse
  263.  * click was done.
  264.  * 
  265.  * Revision 0.51  91/05/29  14:42:52  chua
  266.  * In the TimerNotify procedure (lines 196-199), check to see that the instrument is not muted
  267.  * before procedure to make the message calls to the remote application.
  268.  * 
  269.  * Also, in the play and stop procedures, include the function call to DimButtons to dim/undim
  270.  * the buttons during playback or stop mode.  The dimming of buttons is to prevent errors, such as
  271.  * the user trying to cut some section of the TimeLine while it is playing.
  272.  * 
  273.  * Revision 0.50  91/05/24  16:37:51  chua
  274.  * 
  275.  * 
  276.  * Revision 0.49  91/05/23  17:39:10  chua
  277.  * *** empty log message ***
  278.  * 
  279.  * Revision 0.48  91/05/22  16:42:06  chua
  280.  * Removed the CheckIfNoteSelected function call in the PlaySelectedHandler routine.
  281.  * 
  282.  * Revision 0.47  91/05/22  13:59:39  chua
  283.  * In the PlaySelectedHandler routine, replace the if (startX != endX) statement with
  284.  * if (areaSelected || noteSelected).  The latter is a more correct way of checking if either
  285.  * a note or a region is selected.
  286.  * 
  287.  * Revision 0.46  91/05/22  11:47:22  chua
  288.  * In the PlaySelectedHandler, first check if a note is selected.  Next, instead of checking
  289.  * if the areaSelected variable is set, check for whether startX != endX, an indication that
  290.  * either an area is selected or a note is selected. 
  291.  * 
  292.  * The changes enables the play selected menu to play a selected note as well.
  293.  * 
  294.  * Revision 0.45  91/05/17  16:57:21  chua
  295.  * *** empty log message ***
  296.  * 
  297.  * Revision 0.44  91/05/17  16:41:26  chua
  298.  * Added code to enable the TLE to play a selected region.  This region need not include all open
  299.  * applications and the TLE will just play those applications that have been selected.
  300.  * 
  301.  * In the TimerNotify procedure, instead of using a while loop to go through all the instruments
  302.  * when testing which instrument has a note to be played, a for-loop is now used so that only
  303.  * the instruments selected (which must be sequential, by virtual of the selection paradigm) will
  304.  * be tested.  For the play all and play from insertion point functions, this for-loop will 
  305.  * essentially be the same as the while loop, since all instruments will be tested.
  306.  * For the PlaySelected procedure, the parameters startApp and numApps are used to control the
  307.  * for-loop.  This are obtained by performing some calculations on the startY and endY variables
  308.  * set in the DrawCanvasEventHandler.  The calculations are done in the PlaySelectedHandler
  309.  * function.
  310.  * 
  311.  * Besides filling in the code for the PlaySelectedHandler, a new function, PlayFromPoint is
  312.  * introduced.  The reason is the PlayFromHandler and the PlaySelectedHandler share similar
  313.  * initialization routines, such as testing if scrolling is necessary, initializing the timer etc.
  314.  * So just one function is created instead of duplicating code in both places.
  315.  * 
  316.  * Revision 0.43  91/05/16  15:20:54  chua
  317.  * Added a new menu function to the play button:  Play the selected region (PlaySelectedHandler).
  318.  * Functionality not implemented yet.
  319.  * 
  320.  * Revision 0.42  1991/05/15  02:58:36  chua
  321.  * In the PlayStop function, instead of setting all the timer values to zero and calling the
  322.  * timer notify function again,  replace the parameters in the notify_set_itimer_func call
  323.  * to NOTIFY_FUNC_NULL and NULL in place of NULL and &timer.  This will have the same effect
  324.  * of turning the timer off and takes only one line of code instead of several!
  325.  *
  326.  * Also, in the timer notify function, when calculating the timediff, change the statement
  327.  *     timediff = timediff/100000   to     timediff = (timediff + 50000) / 100000
  328.  *
  329.  * This has the more accurate result of rounding rather than truncating the calculated time,
  330.  * though probably it doesn't really matter, with the larger synchronization problems caused
  331.  * by the slower media.
  332.  *
  333.  * Revision 0.41  1991/04/24  01:12:30  chua
  334.  * *** empty log message ***
  335.  *
  336.  * Revision 0.40  1991/04/01  03:51:30  chua
  337.  * This file contains the timer notify function to perform playback of a timeline document and also the
  338.  * menu handlers for the play menu button.  The functions are:
  339.  * AllocateTimerSpace - allocate memory for the timer variables used to keep track of real time.
  340.  * TimerNotify - Timer notify routine which does the playback of a TimeLine document.
  341.  * PlayHandler - Menu handler for the play menu selection.  Plays from the start of a document.
  342.  * PlayStop - Routine to turn off the timer, thus stopping play and free the timer variables.
  343.  * Stop - Button notify procedure for the Stop button which sets the status flag to StopMode, which will be
  344.  *        detected by the TimerNotify routine and causing PlayStop to be called.
  345.  * PlayFromHandler - Menu handler for the play from menu selection.  Plays from where the playback head is.
  346.  * DrawPlaybackHead - Draws the playback head at the new position and erase the old one.
  347.  * */
  348.  
  349. #include "main.h"
  350.  
  351. static char Playrcsid[] = "$Header: /Source/Media/collab/TimeLine/RCS/play.c,v 1.16 92/10/30 16:22:49 drapeau Exp $";
  352.  
  353. int startApp;                                /* The starting application to be played during playback */
  354. int numApps;                                /* The number of applications to be played during playback */
  355. static long virtualClock;                        /* A simple 'clock' maintained by this application.  It... */
  356.                                     /* ...keeps track of the current position of the... */
  357.                                     /* ...playback head during playback.  The XView timer... */
  358.                                     /* ...notify function is used to periodically... */
  359.                                     /* ...(1/10th of a second) call a function that advances... */
  360.                                     /* ...the playback head */
  361. static TimeLineFramePtr currenttlFrame;                    /* Pointer to current TimeLine document being played */
  362. static struct itimerval timer;                        /* Timer for playback purposes */
  363. static long canvasWidth;                        /* Current width of the canvas window.  This information... */
  364.                                     /* ...is used to facilitate scrolling of the canvas... */
  365.                                     /* ...during playback. */
  366. static long canvasStart;                        /* Current start point of the canvas in the window. */
  367. static long endTime;                            /* Estimated ending time of current document in playback. */
  368. static long endSelectedTime;                        /* The end time for a selected region */
  369. static int status = StopMode;
  370. static int flag = 0;                            /* Indicates what color is to be drawn for the play button */
  371.  
  372. /*
  373.  * This function checks if the playback head is at a pause marker during
  374.  * playback.  The donePause variable is necessary, because after we
  375.  * resume from a pause, the playback head will still be at the same
  376.  * position.  So we compare this with the donePause and if they are the
  377.  * same, ignore the pause marker.
  378.  */
  379. int CheckPause()
  380. {
  381.   Pause *pause;
  382.   int found;
  383.   
  384.   pause = currenttlFrame->pauseHead;                    /* Check if there is a pause */
  385.   found = 0;
  386.   while (pause != NULL && !found) 
  387.   {
  388.     if (pause->position == virtualClock)
  389.     {
  390.       if (pause == currenttlFrame->donePause)                /* Make sure this pause marker is not the old one */
  391.     currenttlFrame->donePause = NULL;
  392.       else 
  393.       {
  394.     found = 1;
  395.     currenttlFrame->donePause = pause;
  396.     PauseResume(currenttlFrame->TimeLine_window->pauseButton, NULL);
  397.     return OK;
  398.       }
  399.     }
  400.     pause = pause->next;
  401.   }
  402.   return Error;                                /* No pause marker found */
  403. }                                    /* end function CheckPause */
  404.  
  405.  
  406. /* 
  407.  * This is the timer notify function that is called by the XView
  408.  * timer notify procedure.  It is called every 1/10th of a second during
  409.  * playback, regardless of the zoom level.  This way, the playback of
  410.  * notes will be accurate regardless of the zoom level.
  411.  *
  412.  * First, BlinkPlayButton() is called to do just that.
  413.  *
  414.  * Next, UpdatePlaybackHead() is called to redraw the playback head
  415.  * and canvas, if necessary.
  416.  *
  417.  * Next, check if we are at a pause marker, done by calling the
  418.  * CheckPause routine.
  419.  *
  420.  * The function next goes through the instrument list one by one.
  421.  * Each instrument has a current note pointer which indicates which note
  422.  * is currently being accessed.  Note that only the instruments selected
  423.  * will be tested for whether there is a note to be played.  
  424.  * The FindNextNoteToPerform() function is called to determine if the
  425.  * current instrument has a note to set up for performance.
  426.  *
  427.  * A finished flag is used to determine if there are any more notes to
  428.  * be processed.  This flag is set to 1 initially before the loop to test
  429.  * if any notes are to be played.  If all the instrument's current note
  430.  * pointer is NULL, this means no more notes need to be played and the
  431.  * flag remains at 1.  Otherwise, the finished flag is set to 0.
  432.  *
  433.  * If such a note is found, the function determines whether it is time
  434.  * to prepare the note for performance.  If so, the function
  435.  * PrepareNoteForPerformance() is called.  If the virtualClock is in the
  436.  * middle of a note and this TimeLine document is in one of the Resume
  437.  * modes, then instead of preparing the current note for performance, a
  438.  * flag is set to indicate that a ResumeSelection message should be sent
  439.  * at the appropriate time, instead of a PerformSelection message.
  440.  *
  441.  * This testing and performance preparation is done for all instruments
  442.  * before calling the SenderPerformSelection.  The purpose is to have all
  443.  * the applications do their seeking first so that they are ready to play
  444.  * at about the same time.
  445.  *
  446.  * If the current instrument has a note ready to play, the playNote
  447.  * flag of that instrument is then set to 1, indicating that this
  448.  * instrument now has a note to be played.  The endTime variable is also
  449.  * updated if necessary, that is, if the end time of the note to be
  450.  * played is later than the current end time.
  451.  * 
  452.  * After all of the instruments have been scanned for ready-to-play
  453.  * notes, the function ResumeOrPerformSelections() is called.  The
  454.  * function will take care of the actual performance of notes at the
  455.  * correct time.  If a document has synchronization hints, it will often
  456.  * be the case that the times for Preparing a selection for performance
  457.  * and actually Performing the selection will differ.
  458.  *
  459.  *
  460.  * After all preparation and performance work is done, a check is made to
  461.  * determine if:
  462.  * 1) the stop button has been pressed;
  463.  * 2) the virtualClock time is greater than the endTime and finished is
  464.  *    set to 1, meaning no more notes to be played (the endTime variable is
  465.  *    necessary so that the playback head will continue moving to the end of
  466.  *    the last note);
  467.  * 3) the virtualClock has reached the end of a selected region, denoted
  468.  *    by the "endSelectedTime" variable, which is set when the playSelected
  469.  *    button is chosen.
  470.  * If either of the above 3 conditions are satisfied, play will be
  471.  * halted.
  472.  */
  473.  
  474. Notify_value TimerNotify()
  475. {
  476.   int            i;
  477.   int            finished;
  478.   int            startTime = 0;
  479.   Instrument*        instrument;
  480.   
  481.   BlinkPlayButton();                            /* Determine whether to change the color of the Play button */
  482.   if (currenttlFrame->status == PauseMode ||                /* Do not update time if in pause mode */ 
  483.       currenttlFrame->status == PauseSelectedMode)
  484.   {
  485.     virtualClock ++;
  486.     return NOTIFY_DONE;
  487.   }
  488.   UpdatePlaybackHead();                            /* Determine whether playback head needs to be redrawn */
  489.   if (CheckPause() == OK)                        /* Pause marker located.  Do not continue. */
  490.     return NOTIFY_DONE;
  491.   instrument = (Instrument *) FindInstrument(startApp, currenttlFrame);
  492.   finished = 1;
  493.   for (i=0; i < numApps; i++, instrument = instrument->next)        /* Looking at one instrument at a time, do the following: */
  494.   {
  495.     if (xv_get(instrument->editInfo->MuteChoice, PANEL_VALUE) == 1) /* If this instrument is muted, don't bother doing work... */
  496.       continue;                                /* ...on it; just go on to the next instrument on the list. */
  497.     if (FindNextNoteToPerform(instrument) == (Note*)NULL)        /* Is there a note to perform right now for this instrument? */
  498.       continue;                                /* No, go on to the next instrument */
  499.     startTime = CalculateStartTime(instrument);                /* Get start time for this note */
  500.     finished = 0;
  501.     if (startTime == virtualClock ||                    /* Is it time to do something?  Test 1 determines whether...*/
  502.     (instrument->currentNote->start <= virtualClock &&        /* ...OpenDoc/SetSelection are to be called; Tests 2 & 3... */
  503.      instrument->currentNote->end > virtualClock))            /* ...determine if clock is in the middle of a note */
  504.     {
  505.       UpdateEndTime(instrument);                    /* Update the global variable "endTime" if necessary */
  506.       if (startTime == virtualClock)
  507.     instrument->currentNote->ms->selection->offset = 0;        /* No offset; playing a complete selection */
  508.       else
  509.     instrument->currentNote->ms->selection->offset =        /* Calculate the offset for a partial selection */
  510.       (virtualClock - instrument->currentNote->start) * 100;
  511.       if (startTime == virtualClock ||                    /* It's time to send OpenDoc & SetSelection messages so... */
  512.       (currenttlFrame->status != ResumeMode &&            /* ...PerformSelection can execute immediately.  Also,... */
  513.        currenttlFrame->status != ResumeSelectedMode))        /* ...TimeLine should not perform a ResumeSelection here. */
  514.       {
  515.     PrepareNoteForPerformance(instrument, startTime);
  516.       }                                    /* end if(startTime == virtualClock...) */
  517.       if (startTime != virtualClock &&                    /* Resume selection is to be done, not perform selection */
  518.       (instrument->currentNote->start != virtualClock) &&
  519.       (currenttlFrame->status == ResumeMode ||
  520.        currenttlFrame->status == ResumeSelectedMode)) 
  521.     instrument->partialNote = 1;
  522.     }                                    /* end if (startTime == virtualClock...) */
  523.   }                                    /* end for... */
  524.   ResumeOrPerformSelections();
  525.   if ((virtualClock >= endTime && finished) ||
  526.       currenttlFrame->status == StopMode)                /* No more notes to be played or stop has been pressed */
  527.   {
  528.     UpdateSyncHints(finished);                        /* Update synchronization hints for this doc, if necessary */
  529.     PlayStop();                                /* Regardless, stop playing the document. */
  530.   }
  531.   else if (virtualClock >= endSelectedTime &&                /* Reached the end of a selected region */
  532.        (currenttlFrame->status == PlaySelectedMode ||
  533.         currenttlFrame->status == ResumeSelectedMode))
  534.   {
  535.     StopPlayingSelection();
  536.   }
  537.   else 
  538.     virtualClock ++;                            /* Increment the clock counter */
  539.   return NOTIFY_DONE;
  540. }                                    /* end function TimerNotify */
  541.  
  542.  
  543. /*
  544.  * Function to turn off the timer and stop playback.
  545.  * Called by TimerNotify (play.c)
  546.  */
  547. void PlayStop()
  548. {
  549.   int        i;
  550.   Instrument*    instrument;
  551.   
  552.   notify_set_itimer_func(currenttlFrame->TimeLine_window->window,   /* Turn timer off */
  553.              NOTIFY_FUNC_NULL, ITIMER_REAL, 
  554.              NULL, NULL);
  555.   if ((autoQuit == True) && (autoPlay == True))                /* Was a command-line option set to quit TL immediately... */
  556.     xv_destroy_safe(currenttlFrame->TimeLine_window->window);        /* ...after the document was performed?  If so, quit. */
  557.   DimButtons(FALSE, currenttlFrame);
  558.   currenttlFrame->donePause = NULL;
  559.   status = StopMode;
  560.   currenttlFrame->status = StopMode;
  561.   instrument = (Instrument *) FindInstrument(startApp, currenttlFrame);
  562.   for (i=0; i < numApps; i++)                        /* Restore synchronous messaging, so that TimeLine will... */
  563.   {                                    /* ...block waiting for completion of messages. */
  564.     SenderSynchronousMessaging(instrument->sender, On);
  565.     instrument = instrument->next;
  566.   }
  567.   xv_set(currenttlFrame->TimeLine_window->playIconButton,
  568.      PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  569.   xv_set(currenttlFrame->TimeLine_window->pauseButton,
  570.      PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  571. }                                    /* end function PlayStop */
  572.  
  573.  
  574. /* This function is called before a play operation.  It will ask all
  575.  * the relevant applications to preload their documents and perform a
  576.  * seek (if applicable) first.
  577.  */
  578.  
  579. void PreLoad() 
  580. {
  581.   Instrument*    instrument;
  582.   int        i, done;
  583.   int        result;
  584.   int        count;
  585.   
  586.   BusyCursor(currenttlFrame);
  587.   instrument = (Instrument *) FindInstrument(startApp, currenttlFrame);
  588.   for (i=0; i < numApps; i++)                        /* Check to see which notes need to be played... */
  589.   {                                    /* ...currently.  If such a note is found, do an... */
  590.     done = 0;                                /* ...open doc and set selection function first */
  591.     while (instrument->currentNote != NULL && !done)            /* Either go through the whole instrument list or a... */
  592.     {                                    /* ...subset of it, depending on startApp and numApps values */
  593.       if (instrument->currentNote->start  < virtualClock &&
  594.       instrument->currentNote->end <= virtualClock)            /* Keep searching while whole note is before virtual clock. */
  595.     instrument->currentNote = instrument->currentNote->next;
  596.       else 
  597.     done = 1;
  598.     }
  599.     if (instrument->currentNote != NULL) 
  600.     {
  601.       if (virtualClock < instrument->currentNote->start) 
  602.     instrument->currentNote->ms->selection->offset = 0;
  603.       else 
  604.     instrument->currentNote->ms->selection->offset =        /* Calculate the offset */
  605.       (virtualClock - instrument->currentNote->start) * 100;
  606.       if (xv_get(instrument->editInfo->MuteChoice, PANEL_VALUE) == 0) 
  607.       {
  608.     if (CheckAppOpen(currenttlFrame, instrument, 0) == OK)        /* If app is no longer open, error message appears... */
  609.     {                                /* ...and the app is muted */
  610.       InstrumentDrawIcon(instrument, Waiting, currenttlFrame);  /* Redraw the icon with a red background */
  611.       XFlush((Display*)xv_get(currenttlFrame->TimeLine_window->window, XV_DISPLAY));
  612.       SenderSynchronousMessaging(instrument->sender, On);        /* Make sure that OpenDoc and SetSelection messages block... */
  613.       SenderOpenDocument (instrument->sender,            /* ...until completion */
  614.                   instrument->currentNote->ms->documentName);
  615.       result = SenderSetSelection (instrument->sender,
  616.                        instrument->currentNote->ms->selection);
  617.       count = 0;
  618.       while (result != 0 && count < PingTimes) 
  619.       {
  620.         result = SenderPing(instrument->sender);
  621.         count ++;
  622.       }
  623.       if (currenttlFrame->syncHints == SyncHintsAvailable)        /* If synchronization information is present,  make sure... */
  624.         SenderSynchronousMessaging(instrument->sender, Off);    /* ...that each Sender is sending messages asynchronously */
  625.       else                                /* If not, make sure that each Sender is blocking for... */
  626.         SenderSynchronousMessaging(instrument->sender, On);        /* ...events like OpenDoc, Set/PerformSelection to finish */
  627.       if (instrument->relativePosition == currenttlFrame->chosenApp) /* Redraw the icon as it was originally */
  628.         InstrumentDrawIcon(instrument, Sunken, currenttlFrame);
  629.       else 
  630.         InstrumentDrawIcon(instrument, Raised, currenttlFrame);
  631.       XFlush((Display*)xv_get(currenttlFrame->TimeLine_window->window, XV_DISPLAY));
  632.     }
  633.       }
  634.     }
  635.     instrument = instrument->next;
  636.   }
  637.   NormalCursor(currenttlFrame);
  638. }                                    /* end function PreLoad */
  639.  
  640. /*
  641.  * Menu handler for `PlayMenu (Play)'
  642.  * This function will initiate the playback routine, starting play from
  643.  * the beginning of the document.
  644.  * 1) Sets all the current note pointer of each instrument to point to
  645.  *    the first note of each instrument.  It then checks if all applications
  646.  *    are still open by attempting to create a new sender.
  647.  * 2) Resets the starting position of the canvas to the start of the
  648.  *    document.
  649.  * 3) Get the width of the canvas window.  This is for scrolling
  650.  *    purposes, to be used by the TimerNotify procedure.
  651.  * 4) Initialize the canvasStart, endTime and virtualClock variables,
  652.  *    and allocate space for the real time variables.
  653.  * 5) Dim the buttons (so that only the Stop button is active).
  654.  * 6) Set the status to PlayMode, initialize the timer values and call
  655.  *    the XView timer notify function.  This function will call the
  656.  *    specified TimerNotify function once every 1/10th of a second.
  657.  */
  658. Menu_item PlayHandler(Menu_item    item, Menu_generate op)
  659. {
  660.   Instrument*            instrument;
  661.   TimeLineFramePtr             tlFrame;
  662.   TimeLine_window_objects*    ip = (TimeLine_window_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  663.   
  664.   if (status == StopMode) 
  665.   {
  666.     tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  667.     currenttlFrame = tlFrame;
  668.     switch (op) 
  669.     {
  670.      case MENU_DISPLAY:
  671.       break;
  672.      case MENU_DISPLAY_DONE:
  673.       break;
  674.      case MENU_NOTIFY:
  675.       instrument = tlFrame->instHead;                    /* Set current note of each instrument to point to 1st note */
  676.       while (instrument != NULL)                    /* Also check that all applications are still alive */
  677.       {
  678.     instrument->currentNote = instrument->firstNote;
  679.     if (CheckAppOpen(tlFrame, instrument, 0) == Error)        /* Check if the application is alive */
  680.       return item;
  681.     instrument->initialNote = 1;
  682.     instrument = instrument->next;
  683.       }
  684.       ScrollToFirstQuarter(tlFrame, 0, 0);                /* Scroll to make the start of the document visible */
  685.       canvasWidth = xv_get(tlFrame->DrawScrollbarHor,            /* Get the width of the canvas, for scrolling purposes */
  686.                SCROLLBAR_VIEW_LENGTH);
  687.       tlFrame->status = PlayMode;                    /* Set the status to PlayMode */
  688.       status = PlayMode;
  689.       canvasStart = 0;                            /* Initialize the playback variables */
  690.       endTime = -1;
  691.       virtualClock = 0;
  692.       startApp = 0;                            /* Indicate starting and # of apps.  Since we are playing... */
  693.       numApps = tlFrame->numberOfApps;                    /* ...all apps, start app is 0, numApps is all the apps open */
  694.       DimButtons(TRUE, tlFrame);
  695.       PreLoad();
  696.       timer.it_value.tv_sec = 0;                    /* Set the timer values to notify every 1/10th of a second */
  697.       timer.it_value.tv_usec = 100000;                    /* TimerNotify() will take care of the actual playback */
  698.       timer.it_interval.tv_sec = 0;
  699.       timer.it_interval.tv_usec = 100000;
  700.       notify_set_itimer_func(tlFrame->TimeLine_window->window,
  701.                  TimerNotify, ITIMER_REAL,
  702.                  &timer, NULL);
  703.       break;
  704.      case MENU_NOTIFY_DONE:
  705.       break;
  706.     }
  707.   }
  708.   return item;
  709. }                                    /* end function PlayHandler */
  710.  
  711. /* 
  712.  * This function performs the initialization necessary to begin play
  713.  * from a certain point on the timeline, whether it is from the insertion
  714.  * point (playback head) or from the start of a selected region.
  715.  *
  716.  * 1) Get the current starting position of the canvas and the width of
  717.  *    the canvas window. These are used for scrolling purposes by the
  718.  *    TimerNotify procedure.  If the starting position is currently not
  719.  *    visible on the canvas, scroll the canvas such that the starting
  720.  *    position is visible.
  721.  * 2) Sets the current note pointer of each instrument to point to
  722.  *    the first note of each instrument.  The TimerNotify procedure will
  723.  *    take care of advancing the current note pointer to the next note
  724.  *    closest to the playback head.
  725.  * 3) Set the status to PlayMode, initialize the timer values and call
  726.  *    the XView timer notify function.  This function will call the
  727.  *    specified TimerNotify function once every 1/10th of a second.
  728.  *
  729.  * Called by PlayFromHandler and PlaySelectedHandler (play.c)
  730.  */
  731.  
  732. void PlayFromPoint(TimeLineFramePtr tlFrame)
  733. {
  734.   Instrument* instrument;
  735.   
  736.   currenttlFrame = tlFrame;
  737.   ScrollToFirstQuarter(tlFrame, tlFrame->lastX, 0);            /* Scroll to make the playback head visible */
  738.   canvasStart = xv_get(tlFrame->DrawScrollbarHor,            /* Get the current starting position of the canvas */
  739.                SCROLLBAR_VIEW_START);
  740.   canvasWidth = xv_get(tlFrame->DrawScrollbarHor,            /* Get the width of the canvas, for scrolling purposes */
  741.                SCROLLBAR_VIEW_LENGTH);
  742.   instrument = tlFrame->instHead;                    /* Set current note of each instrument to point to 1st note */
  743.   while (instrument != NULL) 
  744.   {
  745.     if (tlFrame->status != ResumeMode && tlFrame->status != ResumeSelectedMode) 
  746.     {
  747.       instrument->currentNote = instrument->firstNote;
  748.       instrument->initialNote = 1;
  749.     }
  750.     instrument = instrument->next;
  751.   }
  752.   DimButtons(TRUE, tlFrame);
  753.   if (tlFrame->status != ResumeMode && tlFrame->status != ResumeSelectedMode) 
  754.     PreLoad();
  755.   timer.it_value.tv_sec = 0;                        /* Set the timer values to notify every 1/10th of a second */
  756.   timer.it_value.tv_usec = 100000;                    /* The TimerNotify function will take care of playback */
  757.   timer.it_interval.tv_sec = 0;
  758.   timer.it_interval.tv_usec = 100000;
  759.   notify_set_itimer_func(tlFrame->TimeLine_window->window, TimerNotify, ITIMER_REAL,
  760.              &timer, NULL);
  761. }
  762.  
  763.  
  764.  
  765. /*
  766.  * Menu handler for `PlayMenu (play from)'
  767.  * This function will initiate the playback routine, starting play from
  768.  * where the playback head is.  It first checks if all applications are
  769.  * still open by attempting to create a new sender.  It initializes the
  770.  * start (virtualClock) and end (endTime) times and calls PlayFromPoint
  771.  * to do the other initializations and initiate playing.
  772.  */
  773.  
  774. Menu_item PlayFromHandler(Menu_item    item,
  775.               Menu_generate    op)
  776. {
  777.   Instrument*            instrument;
  778.   TimeLineFramePtr        tlFrame;
  779.   TimeLine_window_objects*    ip = (TimeLine_window_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  780.   
  781.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  782.   if (status == StopMode || tlFrame->status == ResumeMode) 
  783.   {
  784.     switch (op) 
  785.     {
  786.      case MENU_DISPLAY:
  787.       break;
  788.      case MENU_DISPLAY_DONE:
  789.       break;
  790.      case MENU_NOTIFY:
  791.       instrument = tlFrame->instHead;                    /* Set current note of each instrument to point to 1st note */
  792.       while (instrument != NULL)                    /* Also check that all applications are still alive */
  793.       {
  794.     if (CheckAppOpen(tlFrame, instrument, 0) == Error)        /* Check if the application is alive */
  795.       return item;
  796.     instrument = instrument->next;
  797.       }
  798.       if (tlFrame->lastX == -1)                        /* Set playback head to start position if it's not on canvas */
  799.     tlFrame->lastX = 0;
  800.       virtualClock = tlFrame->lastX;                    /* Set starting point to where the playback head is */
  801.       endTime = -1;
  802.       if (tlFrame->status != ResumeMode) 
  803.     tlFrame->status = PlayMode;                    /* Set the status to PlayMode only if not in ResumeMode */
  804.       status = PlayMode;
  805.       startApp = 0;                            /* Indicate starting and # of apps.  Since we are playing... */
  806.       numApps = tlFrame->numberOfApps;                    /* ...all apps, start app is 0 & numApps is all apps open */
  807.       PlayFromPoint(tlFrame);
  808.       break;
  809.      case MENU_NOTIFY_DONE:
  810.       break;
  811.     }
  812.   }
  813.   return item;
  814. }                                    /* end function PlayFromHandler */
  815.  
  816.  
  817.  
  818. /*
  819.  * Menu handler for `PlayMenu (Play selected area)'.
  820.  * This function will play the selected area on the timeline.  It will
  821.  * only play those instruments which are in the selected area.  If no
  822.  * area is selected, nothing happens.  Before playing, it will also check
  823.  * to see that all the applications that are in the selected area are
  824.  * still alive.
  825.  */
  826.  
  827. Menu_item PlaySelectedHandler(Menu_item        item,
  828.                   Menu_generate    op)
  829. {
  830.   int                i;
  831.   Instrument*            instrument;
  832.   TimeLineFramePtr        tlFrame;
  833.   TimeLine_window_objects*    ip = (TimeLine_window_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  834.   
  835.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  836.   if (status == StopMode || tlFrame->status == ResumeSelectedMode)
  837.   {
  838.     switch (op) 
  839.     {
  840.      case MENU_DISPLAY:
  841.       break;
  842.      case MENU_DISPLAY_DONE:
  843.       break;
  844.      case MENU_NOTIFY:
  845.       startApp = tlFrame->startY / (IconHeight + IconGap);        /* Determine which instruments are selected */
  846.       numApps = tlFrame->endY / (IconHeight + IconGap) - startApp;
  847.       instrument = (Instrument *) FindInstrument(startApp, tlFrame);
  848.       for (i=0; i < numApps; i++)                   
  849.       {
  850.     if (CheckAppOpen(tlFrame, instrument, 0) == Error)        /* Check if the application is alive */
  851.       return item;
  852.     instrument = instrument->next;
  853.       }
  854.       if (tlFrame->areaSelected || tlFrame->noteSelected)
  855.       {
  856.     if (tlFrame->status != ResumeSelectedMode) 
  857.     {
  858.       tlFrame->status = PlaySelectedMode;
  859.       DrawPlaybackHead(tlFrame->startX, tlFrame);
  860.     }
  861.     virtualClock = tlFrame->lastX * tlFrame->zoomLevel;        /* Set starting point to where the playback head is */
  862.     endSelectedTime = tlFrame->endX * tlFrame->zoomLevel;
  863.     endTime = virtualClock;
  864.     status = PlaySelectedMode;
  865.     PlayFromPoint(tlFrame);
  866.       }
  867.       break;
  868.      case MENU_NOTIFY_DONE:
  869.       break;
  870.     }
  871.   }
  872.   return item;
  873. }                                    /* end function PlaySelectedHandler */
  874.  
  875.  
  876.  
  877. /*
  878.  * Notify callback function for `playIconButton'.
  879.  * If a note or region has been selected, do a play selected region/note.  Else, play from insertion point.
  880.  */
  881. void Play(item, event)
  882.      Panel_item    item;
  883.      Event        *event;
  884. {
  885.   TimeLineFramePtr tlFrame;
  886.   TimeLine_window_objects    *ip = (TimeLine_window_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  887.   
  888.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  889.   if (tlFrame->status == PauseMode || tlFrame->status == PauseSelectedMode)
  890.     PauseResume(item, event);
  891.   else if (tlFrame->status == StopMode) 
  892.   {
  893.     if (tlFrame->areaSelected == 1 || tlFrame->noteSelected == 1) 
  894.       PlaySelectedHandler(item, MENU_NOTIFY);
  895.     else
  896.       PlayFromHandler(item, MENU_NOTIFY);
  897.   }
  898. }
  899.  
  900.  
  901.  
  902. /*
  903.  * Menu handler for `PlayMenu (Synchronize This Document)'.
  904.  */
  905. Menu_item
  906.   SynchronizeDocument(Menu_item item, Menu_generate op)
  907. {
  908.   TimeLineFramePtr tlFrame;
  909.   TimeLine_window_objects    *ip = (TimeLine_window_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  910.   
  911.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  912.   switch (op)
  913.   {
  914.    case MENU_DISPLAY:
  915.     break;
  916.     
  917.    case MENU_DISPLAY_DONE:
  918.     break;
  919.     
  920.    case MENU_NOTIFY:
  921.     tlFrame->syncHints = GeneratingSyncHints;                /* Set flag saying that sync hints should be generated */
  922.     PlayHandler(item, op);                        /* Begin playback of document & calculate sync hints */
  923.     
  924.     /* gxv_start_connections DO NOT EDIT THIS SECTION */
  925.     
  926.     /* gxv_end_connections */
  927.     
  928.     break;
  929.     
  930.    case MENU_NOTIFY_DONE:
  931.     break;
  932.   }
  933.   return item;
  934. }                                    /* end function SynchronizeDocument */
  935.  
  936.  
  937.  
  938. void BlinkPlayButton()                            /* Determines what color to make the Play button; alternates... */
  939. {                                    /* ...between green and black */
  940.   if (virtualClock % PixelsPerSecond == 0)                /* Is it time to determine whether to change colors? */
  941.   {                                    /* Yes, toggle the color of the Play button */
  942.     if (currenttlFrame->status != StopMode && flag)            /* Highlight the play button in green or black (alternating) */
  943.     {
  944.       xv_set(currenttlFrame->TimeLine_window->playIconButton,
  945.          PANEL_ITEM_COLOR, gcm_color_index("green"), NULL);
  946.       if (currenttlFrame->status != PauseMode &&
  947.       currenttlFrame->status != PauseSelectedMode) 
  948.       {
  949.     xv_set(currenttlFrame->TimeLine_window->pauseButton,
  950.            PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  951.       }
  952.       flag = 0;
  953.     }
  954.     else if (currenttlFrame->status == PauseMode ||
  955.          currenttlFrame->status == PauseSelectedMode) 
  956.     {
  957.       xv_set(currenttlFrame->TimeLine_window->playIconButton,
  958.          PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  959.       xv_set(currenttlFrame->TimeLine_window->pauseButton,
  960.          PANEL_ITEM_COLOR, gcm_color_index("green"), NULL);
  961.       if (flag) 
  962.     flag = 0;
  963.       else
  964.     flag = 1;
  965.     }
  966.     else 
  967.     {
  968.       xv_set(currenttlFrame->TimeLine_window->playIconButton,
  969.          PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  970.       xv_set(currenttlFrame->TimeLine_window->pauseButton,
  971.          PANEL_ITEM_COLOR, gcm_color_index("black"), NULL);
  972.       flag = 1;
  973.     }
  974.   }
  975. }                                    /* end function BlinkPlayButton */
  976.  
  977.  
  978.  
  979. /* UpdatePlaybackHead() -- A function to redraw the the playback head
  980.  * and the TimeLine canvas.
  981.  * This functions checks if time has passed by a pixel's equivalent
  982.  * (depending on the zoom level).  If so, it does the following:
  983.  * - Draw the playback head at the new position.
  984.  * - Check if scrolling is necessary.  When the playback head
  985.  *    reaches 50 pixels from the end of the viewing window, the canvas will
  986.  *    scroll a 'page' and the playback head will be repositioned
  987.  *    about 100 pixels from the start of the window.  This is to allow the
  988.  *    user to see what was going on     before the scrolling point.  If
  989.  *    necessary, the canvas will be remapped to the next segment of the
  990.  *    timeline, if the playback head is near the end of the canvas.
  991.  */
  992.  
  993. void UpdatePlaybackHead()                        /* Determines if it's time to redraw the playback head */
  994. {
  995.   if (virtualClock % currenttlFrame->zoomLevel == 0)            /* Only redraw the playback head when it has moved 1 pixel */
  996.   {
  997.     DrawPlaybackHead(virtualClock, currenttlFrame);            /* Draw the playback head */
  998.     if ((currenttlFrame->lastX / currenttlFrame->zoomLevel) -
  999.     currenttlFrame->canvasStart >= 
  1000.     (canvasStart + canvasWidth - (PixelsPerSecond * 5)))        /* Check if scrolling is necessary */ 
  1001.     {
  1002.       canvasStart = (currenttlFrame->lastX / currenttlFrame->zoomLevel)    /* 100 pixels spacing from the start of the canvas... */
  1003.     - (PixelsPerSecond * 10);                    /* ...to the playback head */
  1004.       if (canvasStart >                            /* Check if the canvas start is beyond the last... */
  1005.       currenttlFrame->TimeLineLength - canvasWidth)            /* ...viewing window of the canvas.  If so, switch to the... */
  1006.       {                                    /* ...next segment of the TimeLine document */
  1007.     currenttlFrame->canvasStart = currenttlFrame->lastX -
  1008.       (PixelsPerSecond * 10 / currenttlFrame->zoomLevel);
  1009.     currenttlFrame->canvasStart = currenttlFrame->canvasStart - 
  1010.       currenttlFrame->canvasStart % (TimeLineInterval * PixelsPerSecond) 
  1011.         + (TimeLineInterval * PixelsPerSecond);
  1012.     canvasStart = 1;
  1013.     ShowNewCanvas(currenttlFrame, 1);
  1014.       }
  1015.       else 
  1016.     xv_set(currenttlFrame->DrawScrollbarHor, SCROLLBAR_VIEW_START, 
  1017.            canvasStart, NULL);
  1018.     }
  1019.   }
  1020. }                                    /* end function UpdatePlaybackHead */
  1021.  
  1022.  
  1023.  
  1024. void PrepareNoteForPerformance(Instrument* instrument, int startTime)
  1025. {
  1026.   int            result = 0;
  1027.   int            count = 0;
  1028.   int            elapsedTime = 0;
  1029.   struct timeval    before, after;
  1030.   
  1031.   if (instrument->initialNote == 1)                    /* Do not seek again if it is the first note to be played. */
  1032.   {
  1033.     instrument->initialNote = 0;
  1034.     instrument->playNote = 1;                        /* Set flag so PerformSelection should be done for this note */
  1035.     return;
  1036.   }
  1037.   if (startTime != virtualClock)                    /* Is it really time to send the OpenDoc and SetSelection... */
  1038.     return;                                /* ...messages?  If not, return without doing anything*/
  1039.   BusyCursor(currenttlFrame);
  1040.   if (currenttlFrame->syncHints == GeneratingSyncHints)            /* Should synchronization hints be calculated? */
  1041.     result = gettimeofday(&before,(struct timezone*)NULL);        /* Yes, get time before calling OpenDoc & SetSelection... */
  1042.   if (CheckAppOpen(currenttlFrame, instrument, 0) != OK)        /* If app is no longer open, error msg appears and... */
  1043.   {                                    /* ...the app is muted */
  1044.     NormalCursor(currenttlFrame);
  1045.     return;
  1046.   }
  1047.   SenderOpenDocument(instrument->sender,
  1048.              instrument->currentNote->ms->documentName);
  1049.   result = SenderSetSelection(instrument->sender,
  1050.                   instrument->currentNote->ms->selection);
  1051.   instrument->playNote = 1;                        /* Set flag so PerformSelection should be done for this note */
  1052.   if (currenttlFrame->syncHints == GeneratingSyncHints)            /* Should synchronization hints be calculated? */
  1053.   {                                    /* Yes, collect sync hints */
  1054.     gettimeofday(&after, (struct timezone*)NULL);            /* Get time of day after OpenDoc & SetSelection complete */
  1055.     elapsedTime = (after.tv_sec - before.tv_sec) * 1000;        /* Calculate time taken to execute the OpenDoc and... */
  1056.     elapsedTime += ((after.tv_usec - before.tv_usec) / 1000);        /* ...SetSelection messages */
  1057.     instrument->currentNote->ms->setupTime = elapsedTime;        /* Save this setup time as a hint for synchronization */
  1058.   }
  1059.   if (currenttlFrame->syncHints != SyncHintsAvailable)            /* If messaging is synchronous, then wait a certain... */
  1060.     while (result != 0 && count < PingTimes)                /* ...amount of time for SetSelection message to complete */
  1061.     {
  1062.       result = SenderPing(instrument->sender);
  1063.       count ++;
  1064.     }
  1065.   NormalCursor(currenttlFrame);
  1066.   return;
  1067. }                                    /* end function PrepareNoteForPerformance */
  1068.  
  1069.  
  1070.  
  1071. void ResumeOrPerformSelections()                    /* Sends PerformSelection or ResumeSelection messages,... */
  1072. {                                    /* ...depending on the state of the current document... */
  1073.   int        i;                            /* ...(whether pause mode or normal play, for example) */
  1074.   int        startTime;
  1075.   Instrument*    instrument;
  1076.   
  1077.   instrument = (Instrument *) FindInstrument(startApp, currenttlFrame);
  1078.   for (i=0; i < numApps; i++, instrument = instrument->next)        /* Go through each instrument to see which has a note... */    
  1079.   {                                    /* ...to be played currently */
  1080.     if (instrument->currentNote == (Note*)NULL)                /* If there is no note to consider for this instrument,... */
  1081.       continue;                                /* ...go on to the next instrument */
  1082.     startTime = instrument->currentNote->start +            /* Determine starting time,  taking into account... */
  1083.       (instrument->currentNote->ms->selection->offset / 100);        /* ...partial selections if necessary */
  1084.     if ((instrument->playNote != 0) &&                    /* Is it time to do the actual performance right now? */
  1085.     (startTime == virtualClock))
  1086.     {                                    
  1087.       if ((xv_get(instrument->editInfo->MuteChoice,            /* If application is muted or not running, skip to... */
  1088.           PANEL_VALUE) != 0) ||                    /* ...the next available application */
  1089.       (CheckAppOpen(currenttlFrame, instrument, 0) != OK))
  1090.     continue;
  1091.       if ((currenttlFrame->status == ResumeMode ||            /* Should a ResumeSelection be sent?  If the current... */
  1092.        currenttlFrame->status == ResumeSelectedMode)        /* ...document is in one of the Resume modes and this... */
  1093.       && instrument->partialNote == 1)                /* ...instrument was marked for partial note playback,... */
  1094.       {                                    /* ...then a ResumeSelection message should be sent. */
  1095.     if (currenttlFrame->syncHints == SyncHintsAvailable)        /* Pause turned off asynchronous communications; restore... */
  1096.       SenderSynchronousMessaging(instrument->sender, Off);        /* ...if synchronization information is present. */
  1097.     SenderResumeSelection(instrument->sender);
  1098.     instrument->partialNote = 0;
  1099.     instrument->playNote = 0;
  1100.       }
  1101.       else
  1102.       {
  1103.     SenderPerformSelection(instrument->sender);            /* If this statement is reached, a PerformSelection msg... */
  1104.     instrument->playNote = 0;                    /* ...should be sent. */
  1105.       }
  1106.     }                                    /* end if ((instrument->playNote...)) */
  1107.   }                                    /* end for */
  1108.   if (currenttlFrame->status == ResumeMode)                /* Indicate that the document is now in one of the normal... */
  1109.     currenttlFrame->status = PlayMode;                    /* ...Play modes, since any "ResumeSelection" messages... */
  1110.   else if (currenttlFrame->status == ResumeSelectedMode)        /* ...that may have been sent have been taken care of. */
  1111.     currenttlFrame->status = PlaySelectedMode;
  1112.   return;
  1113. }                                    /* end function ResumeOrPerformSelections */
  1114.  
  1115.  
  1116.  
  1117. /*
  1118.  * FindNextNoteToPerform() -- Determines if the current instrument has
  1119.  * a note to prepare for performance.
  1120.  *
  1121.  * Essentially, this function compares the start time of the current
  1122.  * instrument's current note to that of the virtualClock.  Since both are
  1123.  * in terms of the pixel position on the canvas, if they are equal, this
  1124.  * means the playback head is now at the start of that note and it should
  1125.  * be played.  A note is also ready to be played if the playback head is
  1126.  * in the middle of the note.  The function calls CalculateStartTime() to
  1127.  * take into account any synchronization hints that might be available
  1128.  * for this document.  If there are, the startTime variable is adjusted
  1129.  * to take into account the time needed by the current note to prepare
  1130.  * for its performance.
  1131.  */
  1132.  
  1133. Note* FindNextNoteToPerform(Instrument* instrument)
  1134. {
  1135.   int    done = 0;
  1136.   int    startTime = 0;
  1137.   
  1138.   while (instrument->currentNote != NULL && !done)            /* Traverse the whole list of notes for this instrument... */
  1139.   {                                    /* ...until a note is found that needs to be played */
  1140.     startTime = CalculateStartTime(instrument);                /* Get start time for this note */
  1141.     if (startTime  < virtualClock &&                    /* Was a note found that needs to be played? */
  1142.     instrument->currentNote->end <= virtualClock)
  1143.       instrument->currentNote = instrument->currentNote->next;        /* No, go on to the next note */
  1144.     else                                /* Yes, break out of the loop and exit this function */
  1145.       done = 1;
  1146.   }
  1147.   return(instrument->currentNote);
  1148. }                                    /* end function FindNextNoteToPerform */
  1149.  
  1150.  
  1151. void UpdateEndTime(Instrument* instrument)                /* Determines whether global variable "endTime" needs to be... */
  1152. {                                    /* ...updated, and does the updating if necessary. */
  1153.   if ((instrument->currentNote->start +
  1154.        instrument->currentNote->ms->duration * 5) > endTime)
  1155.   {                                    /* Check if the endTime needs to be updated */
  1156.     endTime = instrument->currentNote->start +
  1157.       instrument->currentNote->ms->duration * 5;
  1158.   }
  1159.   return;
  1160. }                                    /* end function UpdateEndTime */
  1161.  
  1162.  
  1163.  
  1164. void StopPlayingSelection()
  1165. {
  1166.   Instrument*    instrument;
  1167.   int        i;
  1168.   
  1169.   instrument = (Instrument *) FindInstrument(startApp, currenttlFrame);
  1170.   for (i=0; i < numApps; i++, instrument = instrument->next)    
  1171.   {
  1172.     if (CheckAppOpen(currenttlFrame, instrument, 0) != OK)        /* If app is no longer open, error message appears... */
  1173.       continue;                                /* ...and the app is muted */
  1174.     SenderSynchronousMessaging(instrument->sender, On);            /* Assure that TimeLine blocks until HaltSelection completes */
  1175.     SenderHaltSelection(instrument->sender);
  1176.     PlayStop();
  1177.   }
  1178.   return;
  1179. }                                    /* end function StopPlayingSelection */
  1180.  
  1181.  
  1182.  
  1183. void UpdateSyncHints(int finished)
  1184. {
  1185.   if (virtualClock >= endTime && finished != 0)                /* Was the whole document played? */
  1186.   {                                    /* Yes: if TimeLine was generating sync hints, then... */
  1187.     if (currenttlFrame->syncHints == GeneratingSyncHints)        /* ...flag this document as having sync hints */
  1188.     {
  1189.       currenttlFrame->syncHints = SyncHintsAvailable;
  1190.       currenttlFrame->change = 1;                    /* Document is modified due to addition of sync hints */
  1191.       UpdateHeader(currenttlFrame, 1);
  1192.     }
  1193.   }
  1194.   return;
  1195. }                                    /* end function UpdateSyncHints */
  1196.  
  1197.  
  1198. /*
  1199.  * CalculateStartTime() -- A function to calculate the correct
  1200.  * starting performance time for a note on the TimeLine.
  1201.  * This function determines the correct starting time for the currentNote
  1202.  * of the instrument passed in as argument.  It does so by taking into
  1203.  * account the desired starting time and taking into account any
  1204.  * synchronization hints that might be available for the currentNote.  If
  1205.  * no syncHints are available, then the starting time for this note is
  1206.  * simply that time stored in currentNote->start.  If syncHints are
  1207.  * available, the setupTime field of the currentNote is taken into
  1208.  * account, in effect setting the starting time to earlier than the
  1209.  * desired currentNote->start time.  This is done to take into account
  1210.  * the times various media editors take to prepare their selections for
  1211.  * performance.
  1212.  * The return value is given in units of clock ticks (1/10th of a second).
  1213. */
  1214.  
  1215. int CalculateStartTime(Instrument* instrument)
  1216. {
  1217.   int    setupTime = 0;
  1218.   
  1219.   if (currenttlFrame->syncHints == SyncHintsAvailable)            /* If sync hints available for this doc, take setupTime... */
  1220.     setupTime = instrument->currentNote->ms->setupTime / 100;        /* ...into account, converting into clock ticks (1/10th sec) */
  1221.   return(instrument->currentNote->start - setupTime);
  1222. }                                    /* end function CalculateStartTime */
  1223.